package com.mjk.manager.api.apipost.impl;

import com.mjk.common.base.error.BeeError;
import com.mjk.common.base.response.BeeResponse;
import com.mjk.common.base.work.BaseWork;
import com.mjk.common.core.jdbc.service.plus.idbservice.BeeDBService;
import com.mjk.common.core.jdbc.service.plus.wrap.BeeQueryWrapper;
import com.mjk.common.tools.gson.GsonUtils;
import com.mjk.common.tools.logger.BizLogger;
import com.mjk.common.tools.springextend.BeeApplicationListener;
import com.mjk.common.util.ObjectUtils;
import com.mjk.common.web.getway.constns.GetwayConsts;
import com.mjk.common.web.getway.restful.interceptor.GetWayInterceptorReq;
import com.mjk.manager.amis.core.model.SysMenu;
import com.mjk.manager.api.apipost.ApiPostUtils;
import com.mjk.manager.api.apipost.IApiPostPush;
import com.mjk.manager.api.constns.ApiPostProperties;
import com.mjk.manager.api.dto.ApiPostRequest;
import com.mjk.manager.api.model.ApiModel;
import lombok.Data;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;

/**
 * apipost接口推送
 *
 * @author Administrator
 */
@Configuration
@ConditionalOnProperty(name = "bee.apipost", havingValue = "true")
public class ApiPostPushImpl extends BaseWork implements BeeApplicationListener, IApiPostPush {
    @Autowired
    private ApiPostProperties properties;

    @Autowired
    private BeeDBService beeDBService;

    @Value(value = "${server.port:8080}")
    private String port;

    private Map<String, Object> workMapping = new ConcurrentHashMap<>();

    private Integer MAX_POOL_SIZE = 1;

    private Integer MAX_QUERE_NUMBER = 500;

    private boolean running = false;

    //private String headUrl = "";
    /**
     * 队列
     */
    protected final BlockingQueue<Object> queue = new ArrayBlockingQueue<>(MAX_QUERE_NUMBER);
    /**
     * 线程池
     */
    private final ExecutorService pool = Executors.newFixedThreadPool(MAX_POOL_SIZE, new CustomizableThreadFactory("DefaultSyncApiPost"));

    public ApiPostPushImpl() {
        running = true;
        //headUrl = "http://localhost:"+port;
        iniMethod();
    }

    public void push2ApiPost(ApiModel model, GetWayInterceptorReq req, BeeResponse rsp) {
        if (null != rsp && BeeError.BEE_SUC.getCode().equals(rsp.getCode())) {//只推送成功的
            this.queue.add(new ReqRsp(model, req, rsp));
        }
    }

    /**
     * 同步到apimodel 并同步到 apipost
     * 该方法需要try catch 不然现场会中断
     */
    @SuppressWarnings("unchecked")
    public void sync2Api(ReqRsp request) {
        try {
            ApiModel model = null;
            boolean sync = false;
            if (null == request.getModel()) {
				model = new ApiModel(request.getReq());
            } else {
				sync = true;
				model = request.getModel();
                if (ObjectUtils.isEmpty(model.getMenuId())) {
					model.setMenuId(UUID.randomUUID().toString());
				}
            }
            Object work = workMapping.get(MapUtils.getString(request.getReq().getRestfulParams(), GetwayConsts.INSTANCE).toUpperCase());
            model.setParams(GsonUtils.bean2Json(ApiPostUtils.getApiFieldList(work, request.getReq(), model)));
			//保存model到数据库
			BeeQueryWrapper wrapper = new BeeQueryWrapper(new SysMenu());
            if (!ObjectUtils.isEmpty(model.getModelId())) {
                wrapper.eq(SysMenu::getApipostMenuId, model.getModelId());
            } else {
                //wrapper.like(SysMenu::getInstance, MapUtils.getString(request.getReq().getRestfulParams(), GetwayConsts.INSTANCE));
            }
            List<SysMenu> menus = beeDBService.findManyByWrapper(wrapper);
            if (!CollectionUtils.isEmpty(menus)) {
				model.setModelId(menus.get(0).getApipostMenuId());
            } else {
				model.setModelId("0");
            }

            model.setApiClazz(work.getClass().getName());
			//model.setSyncApiPost("0");//发送过一次，就不再发送
			//设置请求地址
			//if(!model.getUrl().contains(headUrl) && "1".equals(model.getApiType())) {
            model.setUrl(model.getUrl());
            //}
            if (sync) {//如果 api不存在 不同步到apipost
                ApiPostRequest req = ApiPostUtils.buildApiPostRequest(model, request.getRsp(), properties);
				ApiPostUtils.send2ApiPost(req, properties);
            }
            model.setModelId(CollectionUtils.isEmpty(menus) ? "0" : menus.get(0).getApipostMenuId());
            if (ObjectUtils.isEmpty(model.getId())) {//不进行更新，更新只在界面中进行
//				ApiModelExample apiExample = new ApiModelExample();
//				apiExample.createCriteria().andIdEqualTo(model.getId());
//				baseDBService.update(model, apiExample);
				beeDBService.save(model);
            }
        } catch (Exception e) {
            BizLogger.error(e);
        }
    }

    @PostConstruct
    public void iniMethod() {
        pool.execute(() -> {
            while (running) {
                try {
                    sync2Api((ReqRsp) queue.take());
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }

    @PreDestroy
    public void destroy() throws InterruptedException {
        running = false;
        pool.shutdown();
        if (!pool.awaitTermination(5L, TimeUnit.SECONDS)) {
            BizLogger.warn("......AnsyTaskExecute: Exceed max timeout for waiting shutdown, ready force close pool.");
            pool.shutdownNow();
        }
    }

    @Override
    public void doApplicationEvent(ContextRefreshedEvent event) {
        workMapping = getWorkMapping(event);
        BeeQueryWrapper wrapper = new BeeQueryWrapper(new SysMenu());
        List<SysMenu> menus = beeDBService.findManyByWrapper(wrapper);
        if (!CollectionUtils.isEmpty(menus)) {
            for (SysMenu menu : menus) {
                if (ApiPostUtils.APIPOST_PUSH.equals(menu.getSyncApipost())) {
                    if (ObjectUtils.isEmpty(menu.getApipostMenuId())) {
                        menu.setApipostMenuId(UUID.randomUUID().toString().replace("-", ""));
                    }
                    ApiPostRequest req = new ApiPostRequest(menu, properties);
                    ApiPostUtils.send2ApiPost(req, properties);
                    SysMenu tmp = new SysMenu();
                    tmp.setSyncApipost("0");
                    tmp.setApipostMenuId(menu.getApipostMenuId());
                    wrapper = new BeeQueryWrapper(tmp);
                    wrapper.eq(SysMenu::getId, menu.getId());
                    beeDBService.update(wrapper);
                }
            }
        }
    }

    @Override
    public int getOrder() {
        return 2;
    }

    @Data
    private static class ReqRsp {
        public ReqRsp(ApiModel model, GetWayInterceptorReq req, BeeResponse rsp) {
            this.req = req;
            this.rsp = rsp;
            this.model = model;
        }

        private ApiModel model;
        private GetWayInterceptorReq req;
        private BeeResponse rsp;
    }
}
